﻿using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using ChessServer.Common;
using ChessServer.Watcher;
using ChessServer.Log;

public static class MongoHelper
{
    class message
    {
        public string tablename;
        public BsonDocument v;
    }
    private static ConcurrentQueue<message> _que = new ConcurrentQueue<message>();
    private static MongoClient _client;
    private static string _databasename = "mmoapp";
    private static Thread _thread = new Thread(ThreadUpdate);
    public static string MailTableName = "db_game_mail";
    public static string LibaoTableName = "db_game_gift_item";
    public static string PayTableName = "db_pay_log";

    private static bool bStop = false;

    static MongoHelper()
    {
    }

    public static int Init()
    {
        var ip = "";
        var port = 0;
        try {
            OurDebug.Log ( "MongoHelper initting" );
            var _setting = new MongoClientSettings();
            //_setting.MaxConnectionIdleTime = new TimeSpan(1, 0, 0);
            //_setting.MaxConnectionLifeTime = new TimeSpan(2, 0, 0);
            //_setting.ConnectTimeout = new TimeSpan(2, 0, 0);
            _setting.SocketTimeout = new TimeSpan(0, 0, 10);
            _setting.HeartbeatInterval = new TimeSpan(0, 0, 3);
            _setting.HeartbeatTimeout = new TimeSpan(0, 0, 1000);
            _setting.MinConnectionPoolSize = 1;
            //settings.Server = new MongoServerAddress("192.168.0.113", 27017);
            ip = ConfigUtils.GetSetting("MongoPub.ip", "127.0.0.1");
            port = ConfigUtils.GetSetting("MongoPub.port", 27028);
            _databasename = ConfigUtils.GetSetting("MongoPub.dbname", "db_model");
            var account = ConfigUtils.GetSetting("MongoPub.dbaccount", "db_model");
            var password = ConfigUtils.GetSetting("MongoPub.dbpassword", "mongo_m@#$lingJun^^2");

            _setting.Server = new MongoServerAddress(ip, port);
            if (!string.IsNullOrEmpty(account)) {
                _setting.Credentials = new[] {
                MongoCredential.CreateCredential(_databasename, account, password),
            };
            }

            _client = new MongoClient(_setting);
            var dl = _client.ListDatabases().ToList().Select(dc => dc.ToString()).ToList();

            //var serveropentime = Find("db_web_server", new BsonDocument("server_id", GameEnvironment.Setting.ProductServerId), new BsonDocument("server_opentime", 1));
            //GameEnvironment.Setting.StartTime = PubFunc.UtcMSToLocalTime(serveropentime[0]["server_opentime"].AsDouble);

            OurDebug.LogFormat("{0} MongoHelper Initialize ok...ip:{1} port:{2} db:{3}", DateTime.Now.ToString("HH:mm:ss"), ip, port, _databasename);
            _thread.Start();
            return 0;

        }
        catch(Exception ex) {
            OurDebug.LogError(string.Format("Init MongoHelper Error:{0} \n ip:{1} port:{2}", ex, ip, port));
            return 1;
        }
    }

    public static void Stop()
    {
        bStop = true;
    }

    public static MongoClient GetMongoclient()
    {
        return _client;
    }

    private static void ThreadUpdate()
    {
        while (!bStop)
        {
            try
            {
                if (_que.IsEmpty)
                {
                    Thread.Sleep(10);
                    continue;
                }
                message d;
                var database = _client.GetDatabase(_databasename, new MongoDatabaseSettings()
                {
                    GuidRepresentation = GuidRepresentation.PythonLegacy,
                    ReadConcern = ReadConcern.Majority,
                    ReadPreference = ReadPreference.Primary,
                    WriteConcern = WriteConcern.Acknowledged
                });
                while (_que.TryDequeue(out d))
                {
                    var c = database.GetCollection<BsonDocument>(d.tablename);
                    c.InsertOne(d.v, new InsertOneOptions() { BypassDocumentValidation = true});
                }
            }
            catch(Exception ex)
            {
                OurDebug.LogException(ex);
                Thread.Sleep(10);
            }
        }
    }

    /// <summary>
    /// 类型名字就是表名
    /// </summary>
    /// <param name="val"></param>
    public static void AddLog(object val)
    {
        var watch = new RunTimeWatch("AddLog " + val);
        //var d = BsonDocument.Create(val);
        var j = MathUtils.ToJson(val);
        var d = BsonDocument.Parse(j);
        _que.Enqueue(new message() { tablename = val.GetType().Name, v = d});
        watch.Check("add ok");
        watch.Flush(false, 10);
    }

    /// <summary>
    /// 获取某种类型的数据
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="key"></param>
    /// <param name="tablename"></param>
    /// <returns></returns>
    public static t GetData<t>(string key, string tablename, string fieldname = "_id")
    {
        try
        {
            var watch = new RunTimeWatch("GetData " + tablename + " fieldname:" + fieldname);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            watch.Check("GetCollection");
            var data = c.Find(new BsonDocument(fieldname, key)).Limit(1);
            if (data.Count() < 1)
                return default(t);
            var a = data.ToCursor().ToList();
            var ret = MathUtils.ParseJson<t>(a[0].ToJson(new MongoDB.Bson.IO.JsonWriterSettings() { OutputMode = MongoDB.Bson.IO.JsonOutputMode.Strict }));
            watch.Flush(false, 10);
            return ret;}
        catch(Exception ex)
        {
            OurDebug.LogException(ex);
            return default(t);
        }
    }

    public static t GetData<t>(BsonObjectId key, string tablename, string fieldname = "_id")
    {
        try
        {
            var watch = new RunTimeWatch("GetData " + tablename + " fieldname:" + fieldname);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            watch.Check("GetCollection");
            var data = c.Find(new BsonDocument(fieldname, key)).Limit(1);
            if (data.Count() < 1)
                return default(t);
            var a = data.ToCursor().ToList();
            var ret = MathUtils.ParseJson<t>(a[0].ToJson(new MongoDB.Bson.IO.JsonWriterSettings() { OutputMode = MongoDB.Bson.IO.JsonOutputMode.Strict }));
            watch.Flush(false, 10);
            return ret;
        }
        catch (Exception ex)
        {
            OurDebug.LogException(ex);
            return default(t);
        }
    }

    public static t GetData<t>(BsonObjectId key, string strkey, string tablename, string fieldname = "_id")
    {
        try
        {
            var watch = new RunTimeWatch("GetData " + tablename + " fieldname:" + fieldname);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            watch.Check("GetCollection");
            var data = c.Find(new BsonDocument("$or", new BsonArray() { new BsonDocument(fieldname, key), new BsonDocument(fieldname, strkey)})).Limit(1);
            if (data.Count() < 1)
                return default(t);
            var a = data.ToCursor().ToList();
            var ret = MathUtils.ParseJson<t>(a[0].ToJson(new MongoDB.Bson.IO.JsonWriterSettings() { OutputMode = MongoDB.Bson.IO.JsonOutputMode.Strict }));
            watch.Flush(false, 10);
            return ret;
        }
        catch (Exception ex)
        {
            OurDebug.LogException(ex);
            return default(t);
        }
    }

    /// <summary>
    /// 谨慎使用，阻塞
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="tablename"></param>
    /// <param name="fl">传个new的空的就是搜索全部</param>
    /// <returns></returns>
    public static List<t> Find<t>(string tablename, BsonDocument fl)
    {
        var watch = new RunTimeWatch("GetData " + tablename);
        var database = _client.GetDatabase(_databasename);
        var c = database.GetCollection<BsonDocument>(tablename);
        var tt = typeof(t);
        var field_id = tt.GetField("_id");
        var property_id = tt.GetProperty("_id");
        IAsyncCursor<BsonDocument> f;
        if (field_id != null || property_id != null)
            f = c.Find(fl).ToCursor();
        else
            f = c.Find(fl).Project(new BsonDocument("_id", 0)).ToCursor();
        watch.Check("Find");
        var l = f.ToList();
        if (l.Count < 1)
            return new List<t>();
        watch.Flush(false, 10);
        return l.Select(a => MathUtils.ParseJson<t>(a.ToJson(new MongoDB.Bson.IO.JsonWriterSettings() { OutputMode = MongoDB.Bson.IO.JsonOutputMode.Strict }))).ToList();
    }

    public static List<BsonDocument> Find(string tablename, BsonDocument fl, BsonDocument projection)
    {
        var database = _client.GetDatabase(_databasename);
        var c = database.GetCollection<BsonDocument>(tablename);
        var f = c.Find(fl).Project(projection).ToCursor();

        return f.ToList();
    }

    /// <summary>
    /// 谨慎使用，阻塞
    /// </summary>
    /// <typeparam name="t"></typeparam>
    /// <param name="tablename"></param>
    /// <param name="fl"></param>
    /// <returns></returns>
    public static long GetCount(string tablename, BsonDocument fl)
    {
        var watch = new RunTimeWatch("GetCount " + tablename);
        var database = _client.GetDatabase(_databasename);
        var c = database.GetCollection<BsonDocument>(tablename);
        watch.Check("Find");
        var ret = c.Count(fl);
        watch.Flush(false, 10);
        return ret;
    }

    /// <summary>
    /// 异步的修改提交的信息，以_id作为key
    /// </summary>
    /// <param name="o"></param>
    /// <param name="tablename"></param>
    /// <returns></returns>
    public static bool UpdateData(object o, string tablename)
    {
        try
        {
            var watch = new RunTimeWatch("UpdateData 1 " + tablename);
            var j = MathUtils.ToJson(o);
            var doc = BsonDocument.Parse(j);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            var newDocument = new BsonDocument("$set", doc);
            c.UpdateOne(new BsonDocument("_id", doc["_id"]), newDocument, new UpdateOptions() { IsUpsert = true, BypassDocumentValidation = true});

            watch.Check("UpdateOne");
            watch.Flush(false, 10);
            return true;
        }
        catch(Exception ex)
        {
            OurDebug.LogException(ex);
            return false;
        }

    }

    ///// <summary>
    ///// 异步的修改提交的信息，以_id作为key
    ///// </summary>
    ///// <param name="o"></param>
    ///// <param name="tablename"></param>
    ///// <returns></returns>
    //public static bool UpdateData(object o, string tablename)
    //{
    //    try
    //    {
    //        var watch = new RunTimeWatch("UpdateData 1 " + tablename);
    //        var j = MathUtils.ToJson(o);
    //        var doc = BsonDocument.Parse(j);
    //        var database = _client.GetDatabase(_databasename);
    //        var c = database.GetCollection<BsonDocument>(tablename);
    //        var newDocument = new BsonDocument("$set", doc);
    //        c.UpdateOne(new BsonDocument("_id", doc["_id"]), newDocument, new UpdateOptions() { IsUpsert = true, BypassDocumentValidation = true });

    //        watch.Check("UpdateOne");
    //        watch.Flush(false, 10);
    //        return true;
    //    }
    //    catch (Exception ex)
    //    {
    //        OurDebug.LogException(ex);
    //        return false;
    //    }

    //}

    /// <summary>
    /// 异步的修改提交的信息，以_id作为key
    /// </summary>
    /// <param name="o"></param>
    /// <param name="tablename"></param>
    /// <returns></returns>
    public static bool UpdateData(object o, string tablename, string fieldname)
    {
        try
        {
            var watch = new RunTimeWatch("UpdateData 2 " + tablename);
            var j = MathUtils.ToJson(o);
            var doc = BsonDocument.Parse(j);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            var newDocument = new BsonDocument("$set", doc);
            //var ret = c.UpdateOne(newDocument, doc, new UpdateOptions() { IsUpsert = true });
            c.UpdateOne(new BsonDocument(fieldname, doc[fieldname]), newDocument, new UpdateOptions() { IsUpsert = true });

            watch.Check("UpdateOne");
            watch.Flush(false, 10);
            return true;
        }
        catch (Exception ex)
        {
            OurDebug.LogException(ex);
            return false;
        }
    }

    /// <summary>
    /// 异步的修改提交的信息，以_id作为key
    /// </summary>
    /// <param name="o"></param>
    /// <param name="tablename"></param>
    /// <returns></returns>
    public static bool UpdateData(object o, BsonDocument fl, string tablename)
    {
        try
        {
            var watch = new RunTimeWatch("UpdateData 3 " + tablename);
            var j = MathUtils.ToJson(o);
            var doc = BsonDocument.Parse(j);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            var newDocument = new BsonDocument("$set", doc);
            c.UpdateOne(fl, newDocument, new UpdateOptions() { IsUpsert = true, BypassDocumentValidation = true });

            watch.Check("UpdateOne");
            watch.Flush(false, 10);
            return true;
        }
        catch (Exception ex)
        {
            OurDebug.LogException(ex);
            return false;
        }

    }

    public static bool UpdateData(string json, BsonDocument fl, string tablename)
    {
        try
        {
            var watch = new RunTimeWatch("UpdateData 4 " + tablename);
            var database = _client.GetDatabase(_databasename);
            var c = database.GetCollection<BsonDocument>(tablename);
            var newDocument = new BsonDocument("$set", json);
            c.UpdateOne(fl, newDocument, new UpdateOptions() { IsUpsert = true, BypassDocumentValidation = true });

            watch.Check("UpdateOne");
            watch.Flush(false, 10);
            return true;
        }
        catch (Exception ex)
        {
            OurDebug.LogException(ex);
            return false;
        }
    }

    public static async void Test()
    {
        try
        {
            //var v = GetData<JsonMail>("g6nWWAJZqjpE5kkAF", MailTableName);

            //var temp = new Dictionary<string, object>();
            //temp["gamemail_type"] = 2;
            //temp["gamemail_server"] = 5;
            //var maillist = MongoHelper.Find<JsonMail>(MongoHelper.MailTableName, new BsonDocument(temp));

            //v.gamemail_yuanbao = 999;
            //UpdateDataAsync(v, MailTableName);
            //AddLog(new UserChatLog() { GuildID = 100000 + "", SendDate = DateTime.Now, Content = "log test" });
            //var client = new MongoClient("mongodb://112.74.64.205:27028");
            //var database = client.GetDatabase("mmoapp");
            //var collections = database.ListCollections().ToList();
            //var l = new List<IMongoCollection<BsonDocument>>();
            //foreach(var v in collections)
            //{
            //    var a = v.Elements.ToBsonDocument()["names"].ToString();
            //    l.Add(database.GetCollection<BsonDocument>(a));
            //    //Console.WriteLine(v.Names);
            //}

            //var collection = database.GetCollection<BsonDocument>("bar");
            //collection.InsertOne(new BsonDocument("Name", "Jack"));
            ////await collection.InsertOneAsync();
            //var list = collection.Find(new BsonDocument("Name", "Jack")).ToList();

            //foreach (var document in list)
            //{
            //    Console.WriteLine(document["Name"]);
            //}
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}
